package algo.math.evaluator.context.operator;

import java.lang.reflect.Type;
import algo.math.evaluator.base.IBase;
import algo.math.evaluator.base.exceptions.InvalidToken;
import algo.math.evaluator.base.exceptions.UnrecognizedToken;
import algo.math.evaluator.context.EvaluationContext;
import algo.math.evaluator.context.operator.interfaces.IOperator;
import algo.math.evaluator.value.IValue;
import algo.math.evaluator.value.Value;
import algo.math.evaluator.value.IValue.Compare;

/**
 * Represent an arithmetic substraction.
 */
public class Substraction extends OperatorPrimitive {

	private String _name = OperatorSymbol.MINUS.toString();

	private String _value= "-";

	private TokenType _type=TokenType.OPERATOR;

	private int _level=4;

	private int _nbOperands =2;

	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.interfaces.IOperator#getName()
	 */
	public String getName() {
		return _name;	
	}
	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.interfaces.IOperator#getNbOperands()
	 */
	@Override
	public int getNbOperands() {
		return _nbOperands;
	}
	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.interfaces.IOperator#getLevel()
	 */
	public int getLevel() {
		return _level;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.interfaces.IOperator#setLevel(int)
	 */
	public void setLevel(int level) {
		return;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.tokenizer.interfaces.IToken#getType()
	 */
	public TokenType getType() {
		return _type;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.tokenizer.interfaces.IToken#setType(algo.math.evaluator.tokenizer.interfaces.IToken.TokenType)
	 */
	public void setType(TokenType type) {
		return;
	}
	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.tokenizer.interfaces.IToken#setValue(java.lang.String)
	 */
	public void setValue(String value) {
		return;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.interfaces.IOperator#getValue()
	 */
	public String getValue() {
		return _value;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.IComponentObject#getComponentType()
	 */
	public Type getComponentType() {
			return IOperator.class;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.IComponentObject#getComponentId()
	 */
	public String getComponentId() {
			return _value;
	}

	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.IComponentObject#getComponentName()
	 */
	public String getComponentName() {
			return _name;
	}
	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.OperatorPrimitive#resolve(algo.math.evaluator.value.IValue[])
	 */
	@Override
	protected IValue resolve(IValue[] operands) throws UnrecognizedToken, InvalidToken {
		
		IBase b = EvaluationContext.GetInstance().getBase();
		IValue.Compare result = operands[0].compareTo(operands[1]);
		Boolean isNegative = false;
		
		if (result==IValue.Compare.EQUALS)
		{
			IValue r = new Value();
			r.setValue(b.valueOf(0),b);
			return r;
		}
		else if (result==IValue.Compare.INFERIOR)
		{
			isNegative = true;
		}

		if (operands[0].isNegative() && operands[1].isNegative())
		{
			if (operands[0].compareTo(operands[1])==Compare.SUPERIOR)
			{
				operands[0].SetIsNegative(false);
				operands[1].SetIsNegative(false);
				isNegative = false;	
			}
			else
			{	
				IValue temp = operands[1];
				operands[1]= operands[0];
				operands[0]=temp;
				operands[0].SetIsNegative(false);
				operands[1].SetIsNegative(false);
				isNegative = true;	
			}
		}
		else if (operands[0].isNegative() && !operands[1].isNegative())
		{
				operands[0].SetIsNegative(false);
				IValue rValue = new Addition().evaluate(operands);
				rValue.SetIsNegative(true);
				return rValue;

		}
		else if (!operands[0].isNegative() && operands[1].isNegative())
		{
				operands[1].SetIsNegative(false);
				IValue rValue = new Addition().evaluate(operands);
				return rValue;
		}
		
		String [] values = prepare(operands[0],operands[1],b);
		
		String int1= values [0];
		String int2= values [1];
		String dec1= values [2];
		String dec2= values [3];
		
		
		int carry1=0;
		String intValue="",decValue ="";
		int epoch =0;
		
		for (int i =dec1.length()-1; i>=0;i--)
		{
			if (b.weightOf(dec1.charAt(i))< b.weightOf(dec2.charAt(i))+carry1)
			{
				epoch =0;
				for (int y =b.weightOf(dec2.charAt(i))+carry1; b.weightOf(dec2.charAt(i))+(carry1+epoch)<b.weightOf(dec1.charAt(i))+ (b.maxWeight());y++ )
				{
					epoch++;
					if (y==b.maxWeight())
					{
						y =0;
					}
				}
				carry1 =1;
			}
			else
			{
				 epoch =0;
				for (int y =b.weightOf(dec2.charAt(i))+carry1; y<b.weightOf(dec1.charAt(i));y++ )
				{
					epoch++;
				}
				carry1 =0;
			}
			decValue = b.valueOf(epoch) + decValue;
		}
		
		
		for (int i =int1.length()-1; i>=0;i--)
		{
			if (b.weightOf(int1.charAt(i))< b.weightOf(int2.charAt(i))+carry1)
			{
				epoch =0;
				for (int y =b.weightOf(int2.charAt(i))+carry1; b.weightOf(int2.charAt(i))+(carry1+epoch)<b.weightOf(int1.charAt(i))+ (b.maxWeight());y++ )
				{
					
					epoch++;
					
					if (y==b.maxWeight())
					{
						y =0;
					}
				}
				carry1 =1;
			}
			else
			{
				 epoch =0;
				for (int y =b.weightOf(int2.charAt(i))+carry1; y<b.weightOf(int1.charAt(i));y++ )
				{
					epoch++;
				}
				
				carry1 =0;
			}
			intValue = b.valueOf(epoch) + intValue;
		}
		
		IValue r = new Value();
		r.setValue(intValue+'.'+decValue,b);
		if (isNegative)
			r.SetIsNegative(true);
		
		return r;
	}

	/* (non-Javadoc)
	 * @see algo.math.evaluator.context.operator.interfaces.IOperator#evaluate(algo.math.evaluator.value.IValue[])
	 */
	@Override
	public IValue evaluate(IValue[] operands) throws UnrecognizedToken,
			InvalidToken {
		return resolve(operands);
	}
}
